#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <aura.h>
#include <auraplus.h>

struct _LockInfo {
    Aura_Char        m_szName[AURA_MAX_DIR];
    Aura_Int32        m_iSemId;
    Aura_ThreadId     m_iOwerTid;
    Aura_UInt32       m_uOwerCount;
};


Aura_ECode CreateAnonymDir(/*out*/char *pDirName)
{
    Aura_ProcessId iPid;
    int ret;
    Aura_UInt32 i = 0;

    assert(pDirName);

    AuraProcess_GetCurrentId(&iPid);

    do{
        sprintf(pDirName, "/tmp/anon%d_%d", (int)iPid, i++);
        ret = mkdir(pDirName, 0x777);
        if(0 == ret) {
            //PTRACE("Create annoymous dir:%s.\n", pDirName);
            return AURA_NOERROR;
        }
    }while((-1 == ret)&&(EEXIST == errno)&&(0 < i));

    PERROR("Error:Create annoymous dir failed! errno:%d.\
    file:%s. line:%d. \n",
    errno, __FILE__, __LINE__);
    return AURA_E_INVALID_OPERATION;

}

Aura_ECode _CreateLock(/* [out] */ Aura_Handle * phLock)
{
    key_t semKey;
    Aura_ECode ec;
    assert(phLock);
    _LockInfo *pLockInfo;

    pLockInfo = (_LockInfo *)malloc(sizeof(_LockInfo));
    if(AURA_NULL == pLockInfo){
        PERROR("Error:Create Lock failed! \
            file:%s. line:%d. \n",
            __FILE__, __LINE__);
        return AURA_E_OUT_OF_MEMORY;
    }

    ec = CreateAnonymDir(pLockInfo->m_szName);
    if(AURA_FAILED(ec)){
        PERROR("Error:Create Lock failed! \
            file:%s. line:%d. \n",
            __FILE__, __LINE__);
        return AURA_E_INVALID_OPERATION;
    }

    semKey = ftok(pLockInfo->m_szName, 'a');

    pLockInfo->m_iSemId = semget( semKey, 1, IPC_CREAT | IPC_EXCL | 0666 );
    if ( pLockInfo->m_iSemId != -1 ){
        if ( semctl( pLockInfo->m_iSemId, 0, SETVAL, 1 ) == -1  ){
            PERROR("Error:Create Mutex failed! errno:%d.\
            file:%s. line:%d. \n",
            errno, __FILE__, __LINE__);
            semctl( pLockInfo->m_iSemId, 0, IPC_RMID );
            return AURA_E_INVALID_OPERATION;
        }
    }
    else{
        PERROR("Error:Create Mutex failed! errno:%d.\
        file:%s. line:%d. \n",
        errno, __FILE__, __LINE__);
        return AURA_E_INVALID_OPERATION;
    }

    //Reset the Owner Id
    pLockInfo->m_iOwerTid = 0;
    pLockInfo->m_uOwerCount = 0;

    *phLock = (Aura_Handle)pLockInfo;

    return AURA_NOERROR;

}


Aura_ECode _Lock(Aura_Handle hLock)
{
    _LockInfo *pLockInfo;
    struct sembuf sSemB;
    int ret;
    Aura_ThreadId iTid;

    assert(hLock);
    pLockInfo = (_LockInfo *)hLock;

    AuraThread_GetCurrentId(&iTid);

    //Is Owner
    if(iTid == pLockInfo->m_iOwerTid){
        pLockInfo->m_uOwerCount++;
        return AURA_NOERROR;
    }

    sSemB.sem_num = 0;
    sSemB.sem_op  = -1;
    sSemB.sem_flg = SEM_UNDO;

    do{
        ret = semop(pLockInfo->m_iSemId, &sSemB, 1);
        if ((-1  == ret)&&(EINTR != errno)){
            PERROR("Error: InterLock_Lock failed! \
            file:%s. line:%d. \n",
            __FILE__, __LINE__);
            return AURA_E_INVALID_OPERATION;
        }
    } while(-1 == ret);

    //Owner
    AuraThread_GetCurrentId(&pLockInfo->m_iOwerTid);

    return AURA_NOERROR;
}

Aura_ECode _Unlock(
        /* [in] */ Aura_Handle hLock)
{
    _LockInfo *pLockInfo;
    struct sembuf sSemB;
    int ret;
    Aura_ThreadId iTid;

    assert(hLock);
    pLockInfo = (_LockInfo *)hLock;

    AuraThread_GetCurrentId(&iTid);

    assert(iTid == pLockInfo->m_iOwerTid);

    if(0 != pLockInfo->m_uOwerCount){
        pLockInfo->m_uOwerCount--;
        return AURA_NOERROR;
    }

    sSemB.sem_num = 0;
    sSemB.sem_op  = 1;
    sSemB.sem_flg = SEM_UNDO;

    //Reset the Owner Id at the last Unlock.
    pLockInfo->m_iOwerTid = 0;

    ret = semop(pLockInfo->m_iSemId, &sSemB, 1);
    if (ret  == -1){
        PERROR("Error: InterLock_Unlock failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_OPERATION;
    }


    return AURA_NOERROR;
}

Aura_ECode _DestroyLock(
        /* [in] */ Aura_Handle hLock)
{
    _LockInfo *pLockInfo;

    assert(hLock);
    pLockInfo = (_LockInfo *)hLock;

    semctl(pLockInfo->m_iSemId, 0, IPC_RMID );
    rmdir(pLockInfo->m_szName);

    free(pLockInfo);

    return AURA_NOERROR;
}
